#include "windowpreview.h"

WindowPreview::WindowPreview(QQuickItem* parent)
    : QQuickItem(parent),
      background(QColor(255, 255, 255, 255)), // white background
      c(QX11Info::connection())
{
    data.resize(24);
    data = {
    -1.0f, 1.0f,0.0f,0.0f,
     1.0f, 1.0f,1.0f,0.0f,
     1.0f,-1.0f,1.0f,1.0f,
    -1.0f, 1.0f,0.0f,0.0f,
    -1.0f,-1.0f,0.0f,1.0f,
     1.0f,-1.0f,1.0f,1.0f
    };

    connect(this, &QQuickItem::windowChanged, this, &WindowPreview::OnWindowChanged);
    // test only
    setImage("/home/kylin/1.png");
}

uint32_t WindowPreview::winId() const
{
    return m_winId;
}

void WindowPreview::setWinId(uint32_t winid)
{
    m_winId = winid;
}

void WindowPreview::Render()
{
    static bool initGL = InitGL();
    Q_UNUSED(initGL);

    updateWindowPreview();
    QMatrix4x4 trans_;
    float scale_im = (float)image_data.width()/(float)image_data.height();
    float scale_form = (float)width()/(float)height();
    if(scale_im>scale_form)
        trans_.ortho(-1.0f,1.0f,-scale_im/scale_form,scale_im/scale_form,-1.0f,1.0f);
    else
        trans_.ortho(-scale_form/scale_im,scale_form/scale_im,-1.0f,1.0f,-1.0f,1.0f);
    program.setUniformValue("transform",trans_);
    if(!image_data.isNull() && texture) {
        delete texture;
        texture = new QOpenGLTexture(image_data);
        texture->bind();
    }
    glClear(GL_COLOR_BUFFER_BIT);
    glClearColor((GLfloat)background.red()/255.0,(GLfloat)background.green()/255.0,(GLfloat)background.blue()/255.0,(GLfloat)background.alpha()/255.0);
    glDrawArrays(GL_TRIANGLES,0,data.count());
    // only test
    window()->update();
}

void WindowPreview::OnWindowChanged(QQuickWindow* w)
{
    if (w == nullptr) return;
    connect(w, &QQuickWindow::beforeRendering, this, &WindowPreview::Render, Qt::DirectConnection);
    w->setClearBeforeRendering(false);
}

void WindowPreview::Release()
{
    VBO.destroy();
}

bool WindowPreview::InitGL()
{
    QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex,this);
    const char *vertexShaderSource =
    "#version 330 core\n"
    "layout(location=0)in vec2 aPos;\n"
    "layout(location=1)in vec2 aTexCoord;\n"
    "out vec2 TexCoord;\n"
    "uniform mat4 transform;\n"
    "void main()\n"
    "{\n"
    "gl_Position = transform * vec4(aPos,0.0f,1.0f);\n"
    "TexCoord = aTexCoord;\n"
    "}";
    vshader->compileSourceCode(vertexShaderSource);

    QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
    const char *fsrc =
    "#version 330 core\n"
    "out vec4 FragColor;\n"
    "in vec2 TexCoord;\n"
    "uniform sampler2D ourTexture;\n"
    "void main()\n"
    "{FragColor = texture(ourTexture, TexCoord);}";
    fshader->compileSourceCode(fsrc);


    program.addShader(vshader);
    program.addShader(fshader);
    program.bindAttributeLocation("ourTexture",0);
    program.bindAttributeLocation("transform",1);
    program.link();
    program.bind();

    VBO.create();

    VBO.bind();
    VBO.allocate(data.data(),data.count()*sizeof (GLfloat));

    program.enableAttributeArray(0);
    program.setAttributeBuffer(0,GL_FLOAT,0,2,4*sizeof (GLfloat));

    program.enableAttributeArray(1);
    program.setAttributeBuffer(1,GL_FLOAT,2*sizeof (GLfloat),2,4*sizeof (GLfloat));
    // set texture filter
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    connect(window()->openglContext(), &QOpenGLContext::aboutToBeDestroyed, this, &WindowPreview::Release, Qt::DirectConnection);
    return true;
}

void WindowPreview::setImage(QImage &image)
{
    image_data = image.mirrored();
}

void WindowPreview::updateWindowPreview()
{
    xcb_composite_query_version_cookie_t comp_ver_cookie = xcb_composite_query_version(c, 0, 2);
    xcb_composite_query_version_reply_t *comp_ver_reply = xcb_composite_query_version_reply(c, comp_ver_cookie, NULL);
    if (!comp_ver_reply)
    {
        // 返回窗口图标
        return;
    }

    const xcb_setup_t *setup = xcb_get_setup(c);
    xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup);
    xcb_screen_t *screen = screen_iter.data;
    // request redirection of window
    xcb_void_cookie_t cookie = xcb_composite_redirect_window_checked(c, m_winId, XCB_COMPOSITE_REDIRECT_AUTOMATIC);
    int win_h, win_w, win_d;

    xcb_get_geometry_cookie_t gg_cookie = xcb_get_geometry(c, m_winId);
    xcb_get_geometry_reply_t *gg_reply = xcb_get_geometry_reply(c, gg_cookie, NULL);
    if (gg_reply) {
        win_w = gg_reply->width;
        win_h = gg_reply->height;
        win_d = gg_reply->depth;
        free(gg_reply);
    } else {
        return;
    }

    // create a pixmap
    xcb_pixmap_t win_pixmap = xcb_generate_id(c);
    xcb_composite_name_window_pixmap(c, m_winId, win_pixmap);

    // get the image
    xcb_get_image_cookie_t gi_cookie = xcb_get_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, win_pixmap, 0, 0, win_w, win_h, (uint32_t)(~0UL));
    QSharedPointer<xcb_get_image_reply_t> gi_reply(xcb_get_image_reply(c, gi_cookie, NULL));
    if (gi_reply) {
        int data_len = xcb_get_image_data_length(gi_reply.data());
        uint8_t *data = xcb_get_image_data(gi_reply.data());
        if(win_d == 32) {
            QImage img((uchar *)data, win_w, win_h, QImage::Format_ARGB32_Premultiplied);
            image_data = img.copy();
        } else if(win_d == 24) {
            QImage img((uchar *)data, win_w, win_h, QImage::Format_RGB32);
            image_data = img.copy();
        }
    }
    window()->update();
}

// test only
void WindowPreview::setImage(const QString &filename)
{
    if(texture) delete texture;
    image_data = QImage(filename).mirrored();
    texture = new QOpenGLTexture(image_data);
}
